////////////////////////////////////////////////////////////////////////////
//
//  CryEngine Source File.
//  Copyright (C), Crytek, 1999-2011.
//
// -------------------------------------------------------------------------
//  File name:   FlowMusicThemeSelectorNode.cpp
//  Version:     v1.00
//  Created:     08/04/2011 by Thomas Wollenzin
//  Description: This is the implementation of the FlowMusicThemeSelectorNode class
//
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "FlowBaseNode.h"

class CFlowNode_MusicThemeSelector : public CFlowBaseNode<eNCT_Instanced>, IMusicSystemEventListener
{

public:

	//////////////////////////////////////////////////////////////////////////
	CFlowNode_MusicThemeSelector(SActivationInfo* pActInfo)
		: m_fMainPatternCurrentProgress(0.0f),
	    m_fMainPatternPlayTimeInSeconds(0.0f),
			m_fMainPatternRemainingPlayTimeInSeconds(0.0f),
			m_nMainPatternCurrentLoopCount(0),
	    m_bNeedsUpdate(false),
		  m_bNeedsLoopCountUpdate(false),
			m_bThemeStarted(false),
			m_bMoodSwitched(false),
			m_bActivated(false)
	{
	}

	//////////////////////////////////////////////////////////////////////////
	~CFlowNode_MusicThemeSelector()
	{
			if(m_bActivated)
				gEnv->pMusicSystem->RemoveEventListener(this);
	}

	//////////////////////////////////////////////////////////////////////////
	IFlowNodePtr Clone(SActivationInfo* pActInfo)
	{
		return new CFlowNode_MusicThemeSelector(pActInfo);
	}

	enum INPUTS 
	{
		eIN_PLAY,
		eIN_THEME_AND_MOOD,
		eIN_DELAY_IN_SECONDS,
		eIN_KEEP_PREVIOUS_MOOD
	};

	enum OUTPUTS 
	{
		eOUT_STARTED,
		eOUT_LOOP_COUNT,
		eOUT_PROGRESS,
		eOUT_TIME,
		eOUT_TIME_REMAINING
	};

	//////////////////////////////////////////////////////////////////////////
	VIRTUAL void GetConfiguration(SFlowNodeConfig& config)
	{
		static const SInputPortConfig inputs[] = 
		{
			InputPortConfig_Void("Play", _HELP("Play the selected theme in the selected mood!"), "Play"),
			InputPortConfig<string>("theme_and_mood_ThemeAndMood", _HELP("Select a theme and mood!"), "ThemeAndMood"),
			InputPortConfig<int>("DelayInSeconds", _HELP("Set the delay in seconds in which this theme and mood will start!"), "DelayInSeconds"),
			InputPortConfig<bool>("KeepPreviousMood", _HELP("Indicates whether to keep the previous theme's mood or not!"), "KeepPreviousMood"),
			InputPortConfig_Null()
		};

		static const SOutputPortConfig outputs[] = 
		{
			OutputPortConfig_Void("Started", _HELP("Gets set once the theme and mood started playing."), "Started"),
			OutputPortConfig<int>("LoopCount", _HELP("Indicates the current loop count of the pattern playing on the main track."), "LoopCount"),
			OutputPortConfig<float>("Progress", _HELP("Indicates the progress between 0 and 1 of the track currently playing on the main track."), "Progress"),
			OutputPortConfig<float>("Time", _HELP("Indicates the amount of time the current main track pattern has been playing."), "Time"),
			OutputPortConfig<float>("TimeRemaining", _HELP("Indicates the amount of time until the main track pattern ends."), "TimeRemaining"),
			OutputPortConfig_Null()
		};

		config.pInputPorts  = inputs;
		config.pOutputPorts = outputs;
		config.sDescription = _HELP("Theme Selector");
		config.SetCategory(EFLN_APPROVED);
	}

	//////////////////////////////////////////////////////////////////////////
	VIRTUAL void Serialize(SActivationInfo* pActInfo, TSerialize ser)
	{
	}

	//////////////////////////////////////////////////////////////////////////
	VIRTUAL void ProcessEvent(EFlowEvent event, SActivationInfo* pActInfo)
	{
		switch (event)
		{
		case eFE_Initialize:
			{
				break;
			}
		case eFE_Activate:
			{
				if (IsPortActive(pActInfo, eIN_PLAY))
				{
					stack_string const sThemeAndMood(GetPortString(pActInfo, eIN_THEME_AND_MOOD));
					size_t const nPos = sThemeAndMood.find('/');
					stack_string const sTheme(sThemeAndMood.substr(0, nPos));
					stack_string const sMood(sThemeAndMood.substr(nPos+1, sThemeAndMood.size()));
					int const nDelayInSeconds    = GetPortInt(pActInfo, eIN_DELAY_IN_SECONDS);
					bool const bKeepPreviousMood = GetPortBool(pActInfo, eIN_KEEP_PREVIOUS_MOOD);

					if (!sTheme.empty())
					{
						gEnv->pMusicSystem->SetTheme(sTheme.c_str(), true, bKeepPreviousMood, nDelayInSeconds, sMood.c_str());

						if (!bKeepPreviousMood && !sMood.empty() && nDelayInSeconds <= 0)
						{
							gEnv->pMusicSystem->SetMood(sMood.c_str(), true, true);
						}

						// Only now set this node to be regularly updated.
						pActInfo->pGraph->SetRegularlyUpdated(pActInfo->myID, true);

						// And register this instance as listener to the music system.
						gEnv->pMusicSystem->AddEventListener(this);

						m_bActivated = true;
					}
				}

				break;
			}
		case eFE_Update:
			{
				if (m_bNeedsUpdate)
				{
					ActivateOutput(pActInfo, eOUT_PROGRESS, m_fMainPatternCurrentProgress);
					ActivateOutput(pActInfo, eOUT_TIME, m_fMainPatternPlayTimeInSeconds);
					ActivateOutput(pActInfo, eOUT_TIME_REMAINING, m_fMainPatternRemainingPlayTimeInSeconds);
					m_bNeedsUpdate = false;
				}

				if (m_bNeedsLoopCountUpdate)
				{
					ActivateOutput(pActInfo, eOUT_LOOP_COUNT, static_cast<int>(m_nMainPatternCurrentLoopCount));
					m_bNeedsLoopCountUpdate = false;
				}

				if (m_bThemeStarted)
				{
					ActivateOutput(pActInfo, eOUT_STARTED, true);
					m_bThemeStarted = false;
				}

				if (m_bMoodSwitched && !m_bActivated)
				{
					// Don't update this node anymore once a mood-switch occurred but it disabled this node.
					pActInfo->pGraph->SetRegularlyUpdated(pActInfo->myID, false);
				}

				break;
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////
	VIRTUAL void GetMemoryUsage(ICrySizer * s) const
	{
		s->Add(*this);
	}

	//////////////////////////////////////////////////////////////////////////
	VIRTUAL void OnMusicSystemEvent(EMusicSystemCallbackEvent const eEvent, SMusicSystemInfo const& rMusicSystemInfo)
	{
		switch (eEvent)
		{
		case MUSICSYSTEM_EVENT_ON_UPDATE:
			{
				if ((fabsf(rMusicSystemInfo.fMainPatternCurrentProgress - m_fMainPatternCurrentProgress) > FLT_EPSILON) ||
					  (fabsf(rMusicSystemInfo.fMainPatternPlayTimeInSeconds - m_fMainPatternPlayTimeInSeconds) > FLT_EPSILON) ||
						(fabsf(rMusicSystemInfo.fMainPatternRemainingPlayTimeInSeconds - m_fMainPatternRemainingPlayTimeInSeconds) > FLT_EPSILON))
				{
					m_fMainPatternCurrentProgress            = rMusicSystemInfo.fMainPatternCurrentProgress;
					m_fMainPatternPlayTimeInSeconds          = rMusicSystemInfo.fMainPatternPlayTimeInSeconds;
					m_fMainPatternRemainingPlayTimeInSeconds = rMusicSystemInfo.fMainPatternRemainingPlayTimeInSeconds;
					m_bNeedsUpdate                           = true;
				}

				break;
			}
		case MUSICSYSTEM_EVENT_ON_UPDATE_LOOP_COUNT:
			{
				m_nMainPatternCurrentLoopCount = rMusicSystemInfo.nMainPatternCurrentLoopCount;
				m_bNeedsLoopCountUpdate        = true;

				break;
			}
		case MUSICSYSTEM_EVENT_ON_THEME_START:
			{
				m_bThemeStarted = true;

				break;
			}
		case MUSICSYSTEM_EVENT_ON_MOOD_SWITCH:
			{
				// The first mood-switch activates and the second one deactivates this node.
				m_bMoodSwitched = !m_bMoodSwitched;

				if (!m_bMoodSwitched)
				{
					// Remove this instance as listener from the music system as soon as the second mood switch occurred.
					gEnv->pMusicSystem->RemoveEventListener(this);

					m_bActivated = false;
				}

				break;
			}
		case MUSICSYSTEM_EVENT_ON_THEME_END:
			{
				// Deactivate this node as soon as a theme-end event comes in.
				gEnv->pMusicSystem->RemoveEventListener(this);

				m_bActivated    = false;
				m_bMoodSwitched = false;

				break;
			}
		}
	}

private:

	float  m_fMainPatternCurrentProgress;
	float  m_fMainPatternPlayTimeInSeconds;
	float  m_fMainPatternRemainingPlayTimeInSeconds;

	size_t m_nMainPatternCurrentLoopCount;

	bool   m_bNeedsUpdate;
	bool   m_bNeedsLoopCountUpdate;
	bool   m_bThemeStarted;
	bool   m_bMoodSwitched;
	bool   m_bActivated;
};

REGISTER_FLOW_NODE("Music:ThemeSelector", CFlowNode_MusicThemeSelector);
